/**
 * @file       godemool/decoder.cc
 * @brief      Demo messages decoder.
 * @author     ben "boin" bonoeil
 * @date       2015-02
 */

/* Copyright (c) 2007-2015, ben "boin" bonoeil

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:
   * Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
   * Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials provided
   with the distribution.
   * Neither the name of the ben "boin" bonoeil nor the names of its
   contributors may be used to endorse or promote products derived
   from this software without specific prior written permission.

   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE
   GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY
   ben "boin" bonoeil ''AS IS'' AND ANY EXPRESS OR IMPLIED
   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   DISCLAIMED. IN NO EVENT SHALL ben "boin" bonoeil BE LIABLE FOR ANY
   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "godemool/error.h"
#include "godemool/decoder.h"
#include <algorithm> // std::max()

using namespace boin;
using namespace google::protobuf;

Decoder::Decoder()
  : BitStream(), eof(false)
{}

Decoder::~Decoder()
{
}

void Decode(Hl2Demo::Header & header)
{
}

CodecType * Decoder::Decode(Hl2Demo::Chunk & chunk)
{
  CodecType * obj = nullptr;

  // Set the bitstream
  Set(chunk.data,chunk.data);

  // Set the RawData just in case
  rawdata.size = chunk.data;
  rawdata.data = chunk.data;

  // Decode accordingly
  switch (chunk.type) {
  case Hl2Demo::dem_synctick:
    break;

  case Hl2Demo::dem_stop:
    eof = true; break;

  case Hl2Demo::dem_consolecmd:
    DecodeConCommand(concommand);
    obj = &concommand;
    break;

  case Hl2Demo::dem_customdata:
    DecodeCustomData(custom);
    obj = &custom;
    break;

  case Hl2Demo::dem_datatables:
    DecodeDataTables(dattables);
    obj = &dattables;
    break;

  case Hl2Demo::dem_packet:
  case Hl2Demo::dem_signon:
    DecodeDemoPacket(messages);
    messages.seqInp = chunk.seq_inp;
    messages.seqOut = chunk.seq_out;
    obj = &messages;
    break;

  case Hl2Demo::dem_stringtables:
    DecodeStringTables(strtables);
    obj = &strtables;
    break;

  case Hl2Demo::dem_usercmd:
    DecodeUserCommand(usrcommand);
    usrcommand.seqOut = chunk.seq_out;
    obj = &usrcommand;
    break;

  default:
    assert(!"INTERNAL !!! unexpected demo message");
  }

  assert(RemBits() < 8);

  return obj;
}

void Decoder::DecodeSkipAll()
{
  Skip(RemBits());
}

void Decoder::DecodeRawData(RawData & raw)
{
  raw.size = Read32();
  raw.data = PosPtr();
}

void Decoder::DecodeCustomData(CustomData & cdata)
{
  *(RawData *)&cdata = rawdata;
  DecodeSkipAll();
}

void Decoder::DecodeDemoPacket(Messages & msgs)
{
  MsgData mdata;
  msgs.clear();

  for (int i=0; RemBytes() >= 2; ++i) {
    mdata.type = ReadVaU32();
    mdata.size = ReadVaU32();
    mdata.data = PosPtr();
    assert(!mdata.msg);
    Message * msg;

    if (NET_Messages_IsValid(mdata.type))
      msg = GetNetMessage(mdata.type);
    else if (SVC_Messages_IsValid(mdata.type))
      msg = GetSvcMessage(mdata.type);
    else if (mdata.type == 100) {
      // ??? TODO
      msg = nullptr;
    } else {
      msg = nullptr;
      assert(msg);
    }
    Skip(mdata.size * 8);

    if (msg) {
      if (!msg->ParseFromArray(mdata.data, mdata.size))
        throw Error(Error::input, "error parsing message");

      assert(!mdata.msg);
      mdata.msg = msg->New();
      mdata.msg->CopyFrom(*msg);
      msgs.push_back(mdata);
      assert(!mdata.msg);

      switch (mdata.type) {
#define CASE( NAME ) case svc_ ## NAME: DecodeMessage(mdata, *(CSVCMsg_ ## NAME *)msgs.back().msg); break
        CASE (UserMessage);
        CASE (GameEventList);
        CASE (ServerInfo);
#undef CASE
#define CASE( NAME ) case net_ ## NAME: DecodeMessage(mdata, *(CNETMsg_ ## NAME *)msgs.back().msg); break
        CASE (SetConVar);
#undef CASE
      }

      if (mdata.msg)
        msgs.push_back(mdata);
    }
    assert(!mdata.msg);
  }
}

void Decoder::DecodeMessage(MsgData & mdata, const CSVCMsg_UserMessage & usr)
{
  assert (!mdata.msg);
  if (usr.has_msg_type()) {
    mdata.type = usr.msg_type();
    Message * msg = GetUsrMessage(mdata.type);
    if (msg && usr.has_msg_data()) {
      mdata.data = (uint8_t*)usr.msg_data().c_str();
      mdata.size = usr.msg_data().size();
      if (!msg->ParseFromArray(mdata.data, mdata.size))
        throw Error(Error::input, "error parsing usermessage");
      mdata.msg = msg->New();
      mdata.msg->CopyFrom(*msg);
    }
  }
}

void Decoder::DecodeMessage(MsgData & mdata, const CSVCMsg_ServerInfo & svi)
{
  assert (!mdata.msg);
  srvinfo.is_set = true;
  srvinfo.protocol = svi.has_protocol() ? svi.protocol() : 0;
  srvinfo.map.crc = svi.has_map_crc() ? svi.map_crc() : 0;
  srvinfo.map.name = svi.has_map_name() ? strpool[svi.map_name()] : nullptr;
  srvinfo.gamedir = svi.has_game_dir() ? strpool[svi.game_dir()] : nullptr;
  srvinfo.tickrate = 0;
  if (svi.has_tick_interval()) {
    const float itv = svi.tick_interval();
    if (itv > 0)
      srvinfo.tickrate = uint16_t( 1.0f / itv + 0.5f );
  }
}

void Decoder::DecodeMessage(MsgData & mdata, const CNETMsg_SetConVar & scv)
{
  assert (!mdata.msg);
  if (scv.has_convars()) {
    const CMsg_CVars & msgcvars = scv.convars();
    int ncvars = msgcvars.cvars_size();
    cVars.reserve(ncvars);
    for (int i=0; i<ncvars; ++i) {
      const CMsg_CVars_CVar & msgcvar = msgcvars.cvars(i);
      cVars.push_back(CVar(strpool[msgcvar.name()], strpool[msgcvar.value()]));
    }
  }
}

void Decoder::DecodeMessage(MsgData & mdata, const CSVCMsg_GameEventList & gel)
{
  assert (!mdata.msg);
  const int count = gel.descriptors_size();
  int32_t maxid = 0;
  for (int i=0; i<count; ++i) {
    const CSVCMsg_GameEventList_descriptor_t & desc = gel.descriptors(i);
    if (desc.has_eventid())
      maxid = std::max(maxid,desc.eventid());
  }
  events.reserve(maxid+1);
  events.assign(maxid+1,EventDesc());

  for (int i=0; i<count; ++i) {
    const CSVCMsg_GameEventList_descriptor_t & desc = gel.descriptors(i);
    const int nkeys = desc.keys_size();
    uint32_t eid = desc.eventid();
    assert(eid < events.size());
    EventDesc & ed = events[eid] = EventDesc(eid, strpool[desc.name()]);
    for (int j=0; j<nkeys; ++j) {
      const CSVCMsg_GameEventList_key_t & key = desc.keys(j);
      ed.keys.push_back(EventKey(key.type(), strpool[key.name()]));
    }
  }
}

void Decoder::DecodeConCommand(ConCommand & concom)
{
  Read(concom);
}

void Decoder::SetDefault(UserCommand & usrcom)
{
  // TODO: this function should set valid id and tick
  usrcom.id      = 0;
  usrcom.tick    = 0;
  usrcom.buttons = 0;
  usrcom.impulse = 0;
  usrcom.mousedx = usrcom.mousedy = 0;
  usrcom.weapon  = usrcom.subwpn = 0;
  for (int i=0; i<3; ++i)
    usrcom.vec[i] = boin::Vector(0,0,0);
}

void UserCommand::DeltaRead()
{
  BitStream bs(data,size);
  has.any = 0;
  if ( (has.id = bs.Read1()) )
    id = bs.Read32();
  if ( (has.tick = bs.Read1()) )
    tick = bs.Read32();
  has.vect = 0;
  for (int j = 0, m = 1; j < 3; ++j)
    for (int i = 0; i < 3; ++i, m <<= 1)
      if (bs.Read1()) {
        has.vect |= m;
        (&vec[j].x)[i] = bs.ReadFP32();
      }
  if ( (has.buttons = bs.Read1()) )
    buttons = bs.Read32();
  if ( (has.impulse = bs.Read1()) )
    impulse = bs.Read8();
  if ( (has.weapon = bs.Read1()) ) {
    weapon = bs.Read(Hl2Demo::MAX_EDICT_BITS);
    if ( (has.subwpn = bs.Read1()) )
      subwpn = bs.Read(Hl2Demo::WEAPON_SUBTYPE_BITS);
  }
  if ( (has.mousedx = bs.Read1()) )
    mousedx = (int16_t) bs.Read16();
  if ( (has.mousedy = bs.Read1()) )
    mousedy = (int16_t) bs.Read16();
  assert(bs.RemBits() < 8);
}

void Decoder::DecodeUserCommand(UserCommand & usrcom)
{
  *(RawData *)&usrcom = rawdata;
  SetDefault(usrcom);
  usrcom.DeltaRead();
  DecodeSkipAll();
}

void Decoder::DecodeStringData(int j, StringTable::String & str)
{
  int datasize = 0;
  Read(str.name);
  str.data.clear();
  if (Read1()) {
    datasize = Read16();
    if (!datasize)
      throw Error(Error::input,"empty string table");
    str.data.reserve(datasize);
    for (int i=0; i<datasize; ++i)
      str.data.push_back( Read8() );
  }
}

void Decoder::DecodeStringTable(int i, StringTable & table)
{
  std::string tablename;
  int numstrings;

  // Decode table name
  Read(table.name);
  // Decode table strings
  numstrings = Read16();

  table.strdata.reserve(numstrings);
  for (int j=0; j<numstrings; ++j) {
    table.strdata.push_back(StringTable::String());
    DecodeStringData(j, table.strdata.back());
  }

  table.usrdata.clear();
  if ( Read1() ) {
    // Decode user data
    numstrings = Read16();
    table.usrdata.reserve(numstrings);
    for (int j=0; j<numstrings; ++j) {
      table.usrdata.push_back(StringTable::String());
      DecodeStringData(j, table.usrdata.back());
    }
  }
  table.strdata.shrink_to_fit();
  table.usrdata.shrink_to_fit();
}

void Decoder::DecodeStringTables(StringTables & tables)
{
  tables.clear();
  int numtables = Read8();
  tables.reserve(numtables);
  for (int i = 0; i < numtables; ++i) {
    tables.push_back(StringTable());
    DecodeStringTable(i, tables.back());
  }
}

void Decoder::DecodeDataTables(DataTables & tables)
{
  tables.clear();

  // Parse send table (array of CSVCMsg_SendTable)
  for (;;) {
    SendTable dt;
    dt.type = ReadVaU32();
    dt.size = ReadVaU32();
    dt.data = PosPtr();
    Skip(dt.size * 8);

    dt.Parse();
    tables.sendtables.push_back(dt);
    if (tables.sendtables.back().msg.is_end())
      break;
  }

  // Parse Server Class.
  int nclass = Read16();
  tables.srvclasses.reserve(nclass);
  for (int i=0; i<nclass; ++i) {
    ServerClass sc;
    sc.idx = Read16();
    assert (sc.idx < nclass);
    assert (sc.idx == i); // $$$ Not sure test to see if those are in order
    Read(sc.name,256);
    Read(sc.dtname,256);
    bool found = false;
    for (sc.dtidx = 0; sc.dtidx < tables.sendtables.size(); ++sc.dtidx)
      if ((found = sc.dtname == tables.sendtables[sc.dtidx].msg.net_table_name()))
        break;
    if (!found)
      throw Error(Error::input, std::string("missing datatable ") += sc.dtname);
    tables.srvclasses.push_back(sc);
  }

  // Compute the number of bits to encode class-ids (sort of log2(nclass))
  // This is the proper algorythm but somehow Valve manage to f*** this up.
  // 0->0 1->1 2->1 3->2 4->2 5->3
#if (0)
  classid_bits=0;
  if (--nclass >= 0)
    while(++classid_bits, (nclass>>=1));
#else
  // Valve version adds one unnecessary bit for the powers of two.
  // SPOILER ALERT !!! you only need 1 bit to encode 2 values.
  // 0->1 1->1 2->2 3->2 4->3 5->3
  for (classid_bits=1; nclass>>=1; ++classid_bits);
#endif

}

Message * Decoder::GetUsrMessage(int id)
{
  Message * msg = nullptr;

  switch (id) {
#define CASE(NAME) case CS_UM_ ## NAME: msg = &msgUsr_ ## NAME; break
    CASE(VGUIMenu);
    CASE(Geiger);
    CASE(Train);
    CASE(HudText);
    CASE(SayText);
    CASE(SayText2);
    CASE(TextMsg);
    CASE(HudMsg);
    CASE(ResetHud);
    CASE(GameTitle);
    CASE(Shake);
    CASE(Fade);
    CASE(Rumble);
    CASE(CloseCaption);
    CASE(CloseCaptionDirect);
    CASE(SendAudio);
    CASE(RawAudio);
    CASE(VoiceMask);
    CASE(RequestState);
    CASE(Damage);
    CASE(RadioText);
    CASE(HintText);
    CASE(KeyHintText);
    CASE(ProcessSpottedEntityUpdate);
    CASE(ReloadEffect);
    CASE(AdjustMoney);
    // CASE(UpdateTeamMoney); // Not defined
    CASE(StopSpectatorMode);
    CASE(KillCam);
    CASE(DesiredTimescale);
    CASE(CurrentTimescale);
    CASE(AchievementEvent);
    CASE(MatchEndConditions);
    CASE(DisconnectToLobby);
    CASE(PlayerStatsUpdate);
    CASE(DisplayInventory);
    CASE(WarmupHasEnded);
    CASE(ClientInfo);
    CASE(XRankGet);
    CASE(XRankUpd);
    CASE(CallVoteFailed);
    CASE(VoteStart);
    CASE(VotePass);
    CASE(VoteFailed);
    CASE(VoteSetup);
    CASE(ServerRankRevealAll);
    CASE(SendLastKillerDamageToClient);
    CASE(ServerRankUpdate);
    CASE(ItemPickup);
    CASE(ShowMenu);
    CASE(BarTime);
    CASE(AmmoDenied);
    CASE(MarkAchievement);
    CASE(MatchStatsUpdate);
    CASE(ItemDrop);
    CASE(GlowPropTurnOff);
    CASE(SendPlayerItemDrops);
    CASE(RoundBackupFilenames);
    CASE(SendPlayerItemFound);
    CASE(ReportHit);
#undef CASE
  }
  assert(msg);
  if (msg) msg->Clear();
  return msg;
}

Message * Decoder::GetNetMessage(int id)
{
  Message * msg = nullptr;

  switch (id) {
#define CASE(NAME) case net_ ## NAME: msg = &msgNet_ ## NAME; break
    CASE(NOP);
    CASE(Disconnect);
    CASE(File);
    CASE(SplitScreenUser);
    CASE(Tick);
    CASE(StringCmd);
    CASE(SetConVar);
    CASE(SignonState);
#undef CASE
  }
  assert(msg);
  if (msg) msg->Clear();
  return msg;
}

Message * Decoder::GetSvcMessage(int id)
{
  Message * msg = nullptr;

  switch (id) {
#define CASE(NAME) case svc_ ## NAME: msg = &msgSvc_ ## NAME; break
    CASE(ServerInfo);
    CASE(SendTable);
    CASE(ClassInfo);
    CASE(SetPause);
    CASE(CreateStringTable);
    CASE(UpdateStringTable);
    CASE(VoiceInit);
    CASE(VoiceData);
    CASE(Print);
    CASE(Sounds);
    CASE(SetView);
    CASE(FixAngle);
    CASE(CrosshairAngle);
    CASE(BSPDecal);
    CASE(SplitScreen);
    CASE(UserMessage);
    CASE(EntityMessage);
    CASE(GameEvent);
    CASE(PacketEntities);
    CASE(TempEntities);
    CASE(Prefetch);
    CASE(Menu);
    CASE(GameEventList);
    CASE(GetCvarValue);
    CASE(PaintmapData);
    CASE(CmdKeyValues);
    CASE(EncryptedData);
#undef CASE
  }
  assert(msg);
  if (msg) msg->Clear();
  return msg;
}

Message * Decoder::GetClcMessage(int id)
{
  Message * msg = nullptr;

  switch (id) {
#define CASE(NAME) case clc_ ## NAME: msg = &msgClc_ ## NAME; break
    CASE(ClientInfo);
    CASE(Move);
    CASE(VoiceData);
    CASE(BaselineAck);
    CASE(ListenEvents);
    CASE(RespondCvarValue);
    CASE(FileCRCCheck);
    CASE(LoadingProgress);
    CASE(SplitPlayerConnect);
    // CASE(ClientMessage); // Not defined
    CASE(CmdKeyValues);
#undef CASE
  }
  assert(msg);
  if (msg) msg->Clear();
  return msg;
}
